﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics;
using System.Xml;
using System.Windows.Forms;
using System.Xml.Serialization;

namespace PWAsker
{
    //This class is only used for fallback operations
    public class Configuration
    {
        //Public objects
        
        //private objects
        private int iActiveProfile = 0;

        //serialized objects + setter/getter
        private String _Configuration_Version;
        public String Configuration_Version
        {
            get { return _Configuration_Version; }
            set { _Configuration_Version = value; }
        }

        private Boolean _EndOnSucessfullExecute = false;
        public Boolean EndOnSucessfullExecute
        {
            get { return _EndOnSucessfullExecute; }
            set { _EndOnSucessfullExecute = value; }
        }
        
        private List<Profile> _lProfiles = new List<Profile>();
        public List<Profile> lProfiles
        {
            get { return _lProfiles; }
            set { _lProfiles = value; }
        }

        //default constructor
        public Configuration()
        {
            Debug.WriteLine("New Configuration-object generated. Dump: " + "Configuration_Version: " + Configuration_Version + " ~ iActiveProfile " + iActiveProfile + " ~ EndOnSucessfullExecute " + EndOnSucessfullExecute + " ~ Listsize: " + _lProfiles.Count);
        }

        //returns all executables from the active profile as strings
        public List<String> getCurrentCommands()
        {
            return _lProfiles[iActiveProfile].getCurrentCommands();
        }

        //returns all executables from the active profile as objects
        public List<Executable> getCurrentExecutables()
        {
            return _lProfiles[iActiveProfile].getCurrentExecutables();
        }

        //adds an executable to the active Profile and Action
        public void addExecutable(Executable objExecutable)
        {
            Debug.WriteLine("Adding new executable to Profile: " + _lProfiles.ElementAt(iActiveProfile).ProfileName);
            _lProfiles.ElementAt(iActiveProfile).addExecutable(objExecutable);
        }

        //deletes all executables of the active Profile and Action
        public void clearExecutables()
        {
            _lProfiles.ElementAt(iActiveProfile).clearCurrentExecutables();
        }

        public void debugDump()
        {
            Debug.WriteLine("Content of Configuration-object: " + "Configuration_Version: " + Configuration_Version + " ~ iActiveProfile " + iActiveProfile + " ~ EndOnSucessfullExecute " + EndOnSucessfullExecute + " ~ Listsize: " + _lProfiles.Count);
        }

        //static method to serialize a Configuration_V2 object to szFilename
        public static void writeConfiguration(Configuration config, String szFilename)
        {
            //If file exists, erase it
            if (File.Exists(szFilename))
            {
                Debug.WriteLine("Old config file found at " + szFilename + ". Deleting it");
                File.Delete(szFilename);
            }
            
            XmlSerializer serializer = new XmlSerializer(config.GetType());
            XmlTextWriter xmlWriter = new XmlTextWriter(szFilename, Encoding.Unicode);
            xmlWriter.Formatting = Formatting.Indented;

            xmlWriter.WriteStartDocument();

            // Write comments
            xmlWriter.WriteComment("This is PWAsker configuration file version 2");
            xmlWriter.WriteComment("The symbol " + MainWindow.PW_REGEX + " will be replaced by the password entered in PWAsker");

            //Serializing the given object
            serializer.Serialize(xmlWriter, config);

            // Ends the document.
            xmlWriter.WriteEndDocument();

            // close writer
            xmlWriter.Close();
        }

        //static method to unserialize a Configuration_V2 object from szFilename
        public static Configuration readConfiguration(String szFilename)
        {
            Configuration config = new Configuration();
            XmlSerializer serializer = new XmlSerializer(typeof(Configuration));
            XmlTextReader xmlReader = new XmlTextReader(szFilename);

            if (File.Exists(szFilename) == false)
            {
                if (MessageBox.Show("Configuration-file not found! Should the default one be crated?", "Configfile not found!", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
                {
                    Debug.WriteLine("Config File does not exist. Creating default");
                    
                    //Generating default config and writing it to disk
                    Configuration newConfig = generateDefaultConfig();
                    writeConfiguration(newConfig, szFilename);
                    return newConfig;
                }
                else
                {
                    Debug.WriteLine("Config File does not exist. User decided to do nothing about this.");
                    return null;
                }
            }

            if (serializer.CanDeserialize(xmlReader))
            {
                config = (Configuration)serializer.Deserialize(xmlReader);
            }
            else
            {
                Debug.WriteLine("Unable to parse " + szFilename);
                MessageBox.Show("Unable to parse the configuration file", "Error parsing configfile", MessageBoxButtons.OK, MessageBoxIcon.Error);
                xmlReader.Close();
                return null;
            }

            xmlReader.Close();

            //Detecting Configuration_V1-File
            if (config.Configuration_Version == null)
            {
                Debug.WriteLine("No configuration-version given. Assuming its config v1");

                //Delete current (invalid) config
                config = null;

                Boolean PWAskerCommentFound = false;
                Boolean confNodeFound = false;

                xmlReader = new XmlTextReader(szFilename);
                while (xmlReader.Read())
                {
                    xmlReader.MoveToElement();

                    if (xmlReader.NodeType == XmlNodeType.Element)
                    {
                        if (xmlReader.Name == "Configuration")
                        {
                            confNodeFound = true;
                        }
                    }

                    if (xmlReader.NodeType == XmlNodeType.Comment)
                    {
                        if (xmlReader.Value == "This is PWAsker configuartion file" || xmlReader.Value == "This is PWAsker configuration file")
                        {
                            PWAskerCommentFound = true;
                        }
                    }

                }
                xmlReader.Close();

                if (PWAskerCommentFound && confNodeFound)
                {
                    Debug.WriteLine("File " + szFilename + " seens to be PWAsker Configuration_V1 file");

                    if (MessageBox.Show("Configuration-file seens to be a PWAsker-configuration file version 1.\r\nTry to convert it?", "Configfile is wrong version!", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
                    {
                        //Importing a v1-config
                        Configuration newConfig = convertV1File(szFilename);
                        writeConfiguration(newConfig, szFilename);
                        newConfig.debugDump();
                        return newConfig;
                    }

                }

                Debug.WriteLine("Unable to parse " + szFilename);
                MessageBox.Show("Unable to parse the configuration file", "Error parsing configfile", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return null;
            }
            
            config.debugDump();
            return config;
        }

        //generates a default configuration-file
        public static Configuration generateDefaultConfig()
        {
            Configuration conf = new Configuration();

            conf.Configuration_Version = "2.0";
            conf.lProfiles.Add(new Profile("Default-Profile"));
            conf.lProfiles[0].lActions.Add(new Action ("Default-Action"));
            conf.addExecutable(new Executable("cmd /k echo Welcome to PWAsker v2", false, 0));

            Debug.WriteLine("Sucessfully generated a new default configuration");

            return conf;
        }

        //Reading in a version 1 config
        public static Configuration convertV1File(string szFilename)
        {
            Debug.WriteLine("Starting to read in a version 1 config from file: " + szFilename);

            Configuration conf = new Configuration();
            Boolean ConfNode = false;

            conf.lProfiles.Add(new Profile("Default-Profile"));
            conf.lProfiles[0].lActions.Add(new Action("Default-Action"));

            XmlTextReader textReader = new XmlTextReader(szFilename);

            while (textReader.Read())
            {
                textReader.MoveToElement();

                //triggers for 'configuration'-section
                if (textReader.NodeType == XmlNodeType.Element)
                {
                    if (textReader.Name == "Configuration")
                    {
                        ConfNode = true;
                    }
                }

                else if (textReader.NodeType == XmlNodeType.EndElement)
                {
                    if (textReader.Name == "Configuration")
                    {
                        ConfNode = false;
                    }
                }

                if (ConfNode == true)
                {
                    if (textReader.NodeType == XmlNodeType.Element)
                    {
                        if (textReader.Name == "Executable")
                        {
                            if (textReader.Read())
                            {
                                textReader.MoveToElement();
                                if (textReader.NodeType == XmlNodeType.Text)
                                {
                                    //Debug.WriteLine("Adding " + textReader.Value);
                                    conf.addExecutable(new Executable(textReader.Value, false, 0));
                                }
                            }
                        }
                    }
                }

            }
            textReader.Close();
            textReader = null;

            Debug.WriteLine("Read in completed. Commands added:\r\n" + conf.getCurrentExecutables() + "\r\n------------------------");
            
            return conf;
        }

    }
}
